#pragma region Copyright
/// <copyright>
/// Copyright (c) 2007 Ilia Shramko.
/// Released under the terms of the GNU Lesser General Public License.
/// </copyright>
#pragma endregion

#include "stdafx.h"

using namespace SharedMemory;

const wchar_t* OSHSource::BitmapNumberVariableName = L"BitmapNumber";
const wchar_t* OSHSource::BitmapSizeVariableName = L"BitmapSize";
const wchar_t* OSHSource::BitmapBufferVariableFormat = L"Bitmap%d";
const wchar_t* OSHSource::BitmapRefCountVariableFormat = L"Bitmap%dRefCount";
const wchar_t* OSHSource::BitmapTimeVariableFormat = L"Bitmap%dTime";

OSHSource::OSHSource() : _sharedMemory(NULL), _visible(false),
	_lastActiveBitmapNumber(OSHSource::InvalidBitmapNumber), _saveOnBitmapClose(false)
{
	ATLTRACE_TS(atlTraceOSHSource, TraceLevel::Verbose,
		"OSHSource::OSHSource() : 0x%08X\r\n", this);
}

OSHSource::~OSHSource()
{
	ATLTRACE_TS(atlTraceOSHSource, TraceLevel::Verbose,
		"OSHSource::~OSHSource() : 0x%08X\r\n", this);
	Close(true);
}

STDMETHODIMP OSHSource::Create(BSTR name, int maxNumberOfBitmaps, int maxBitmapWidth, int maxBitmapHeight)
{
	HRESULT hr = E_FAIL;

	if(IsOpened()) return E_UNEXPECTED;

	if(name == NULL) return E_INVALIDARG;
	if(maxNumberOfBitmaps <= 0) return E_INVALIDARG;
	if(maxBitmapWidth <= 0 || maxBitmapHeight <= 0) return E_INVALIDARG;

	if(OSHSource::InvalidBitmapNumber > (MAXDWORD - maxNumberOfBitmaps))
		return E_INVALIDARG;
		
	_maxNumberOfBitmaps = maxNumberOfBitmaps;
	DWORD size = (maxNumberOfBitmaps * maxBitmapWidth * maxBitmapHeight * 4) +
		(maxNumberOfBitmaps * 1000) + 1000;

	_sharedMemory = new CSharedMemory(name, size);
	if(_sharedMemory == NULL) return E_OUTOFMEMORY;

	do
	{
		if(!_sharedMemory->IsCreated() || !_sharedMemory->AmIFirst())
			break;

		if(!_sharedMemory->AddDwordValue(OSHSource::BitmapNumberVariableName, OSHSource::InvalidBitmapNumber))
			break;

		_firstBitmapNumber = ::GetTickCount() / 2;
		if(_firstBitmapNumber == OSHSource::InvalidBitmapNumber)
			_firstBitmapNumber = OSHSource::InvalidBitmapNumber + 1;

		if(!_sharedMemory->AddDwordValue(OSHSource::BitmapSizeVariableName, 0))
			break;

		hr = AllocateBitmapVariables(CSize(maxBitmapWidth, maxBitmapHeight),
			_maxNumberOfBitmaps, _firstBitmapNumber);
	}
	while(FALSE);


	if(FAILED(hr))
	{
		delete _sharedMemory;
		_sharedMemory = NULL;
	}

	ATLTRACE_TS(atlTraceOSHSource, TraceLevel::Verbose, 
		"OSHSource::Create() : 0x%08X\r\n", hr);

	if(SUCCEEDED(hr))
		_isOwner = true;

	return hr;
}

//STDMETHODIMP OSHSource::SetBitmapSize(int width, int height)
//{
//	HRESULT hr = EnsureBitmapSize(CSize(width, height));
//	return hr;
//}
//
STDMETHODIMP OSHSource::Open(VARIANT clientOrName)
{
	if(IsOpened())
		return E_UNEXPECTED;

	HRESULT hr = S_OK;

	CComBSTR name;
	if(clientOrName.vt == VT_BSTR)
		hr = name.AssignBSTR(clientOrName.bstrVal);
	else if(clientOrName.vt == VT_LPWSTR)
		hr = name.Append((LPWSTR)clientOrName.bstrVal);
	else if(clientOrName.vt == VT_UNKNOWN || clientOrName.vt == VT_DISPATCH)
	{
		CComPtr<OSHLibrary::IClient> client;
		hr = clientOrName.punkVal->QueryInterface(&client);
		if(FAILED(hr)) return hr;

		hr = client->get_SourceName(&name);
	}

	if(FAILED(hr)) return hr;

	hr = OpenSharedMemory(name);
	if(FAILED(hr)) return hr;

	hr = StartMonitoring();
	if(FAILED(hr))
		CloseSharedMemory();

	ATLTRACE_TS(atlTraceOSHSource, TraceLevel::Verbose, 
		"OSHSource::Open() : 0x%08X\r\n", hr);

	return hr;
}

STDMETHODIMP OSHSource::Close()
{
	HRESULT hr = Close(false);
	
	ATLTRACE_TS(atlTraceOSHSource, TraceLevel::Verbose, 
		"OSHSource::Close() : 0x%08X\r\n", hr);

	return hr;
}

HRESULT OSHSource::Close(bool destruction)
{
	if(!destruction)
	{
		if(_closeEvent != NULL)
		{
			_closeEvent.Set();
			::Sleep(1);
			_closeEvent.Close();
		}
	}

	HRESULT hr = StopMonitoring();
	hr;
	return CloseSharedMemory();
}

bool OSHSource::IsOpened()
{
	return _sharedMemory != NULL;
}

STDMETHODIMP OSHSource::get_State(OSHLibrary::StateEnum* state)
{
	if(state == NULL)
		return E_POINTER;
	
	if(IsOpened())
		(*state) = OSHLibrary::oshStateOpen;
	else
		(*state) = OSHLibrary::oshStateClosed;
	
	ATLTRACE_TS(atlTraceOSHSource, TraceLevel::Verbose, 
		"OSHSource::get_State() : %d\r\n", *state);

	return S_OK;
}

STDMETHODIMP OSHSource::get_Visible(VARIANT_BOOL* pVal)
{
	if(pVal == NULL) return E_POINTER;

	if(!IsOpened())
		return E_UNEXPECTED;

	*pVal = _visible ? VARIANT_TRUE : VARIANT_FALSE;

	ATLTRACE_TS(atlTraceOSHSource, TraceLevel::Verbose, 
		"OSHSource::get_Visible() = %s\r\n", *pVal == VARIANT_TRUE ? "TRUE" : "FALSE");

	return S_OK;
}

STDMETHODIMP OSHSource::put_Visible(VARIANT_BOOL newVal)
{
	ATLTRACE_TS(atlTraceOSHSource, TraceLevel::Info,
		"OSHSource::put_Visible(%s)\r\n", newVal == VARIANT_TRUE ? "TRUE" : "FALSE");

	if(!IsOpened())
		return E_UNEXPECTED;

	bool newValue = (newVal == VARIANT_TRUE);
	if(newValue == _visible)
		return S_OK;

	_visible = newValue;
	if(_visible)
		ActivateBitmaps();
	else
		DeactivateBitmaps();

	return S_OK;
}

STDMETHODIMP OSHSource::CreateBitmap(int width, int height, OSHLibrary::IBitmap** ppBitmap)
{
	if(!IsOpened() || !_isOwner)
		return E_UNEXPECTED;

	HRESULT hr = CheckBitmapSize(width, height);
	if(FAILED(hr)) return hr;

	// Find an empty bitmap slot and initialize it
	DWORD bitmapNumber = OSHSource::InvalidBitmapNumber;
	BITMAPINFOHEADER* bitmapHeader = NULL;
	void* bitmapData = NULL;
	hr = GetLastUsedBitmapSlot(&bitmapNumber, &bitmapHeader, &bitmapData);
	if(FAILED(hr)) return hr;

	bitmapHeader->biWidth = width;
	bitmapHeader->biHeight = height;
	bitmapHeader->biPlanes = 1;
	bitmapHeader->biBitCount = 32;
	bitmapHeader->biCompression = BI_RGB;
	bitmapHeader->biSizeImage = width * height * 4;

	// Craete a new Bitmap COM object.
	CComObject<OSHBitmap>* bitmapObject = NULL;
	hr = CComObject<OSHBitmap>::CreateInstance(&bitmapObject);
	if(FAILED(hr)) return hr;

	CComPtr<OSHLibrary::IBitmap> bitmap;
	bitmap = bitmapObject;

	SYSTEMTIME time;
	::GetSystemTime(&time);
	hr = bitmapObject->Initialize(this, bitmapNumber, bitmapHeader, &time);
	if(FAILED(hr)) return hr;

	hr = bitmap.CopyTo(ppBitmap);

	ATLTRACE_TS(atlTraceOSHBitmap, TraceLevel::Verbose,
		"OSHSource::CreateBitmap() = 0x%08X : 0x%08X\r\n", bitmapObject, hr);

	return hr;
}

STDMETHODIMP OSHSource::ShowAndCloseBitmap(OSHLibrary::IBitmap* ppBitmap)
{
	if(ppBitmap == NULL) return E_INVALIDARG;

	if(!IsOpened() || !_isOwner)
		return E_UNEXPECTED;

	_saveOnBitmapClose = true;
	HRESULT hr = ppBitmap->Close();
	_saveOnBitmapClose = false;

	ATLTRACE_TS(atlTraceOSHSource, TraceLevel::Verbose, 
		"OSHSource::ShowAndCloseBitmap() : 0x%08X\r\n", hr);

	return hr;
}


/// <summary>
/// Opens the specified shared memory.
/// If there is no shared memory with the specified name method returns E_FAIL and does not create one.
/// </summary>
HRESULT OSHSource::OpenSharedMemory(const wchar_t* name)
{
	HRESULT hr = S_OK;

	if(!_closeEvent.Create(NULL, FALSE, FALSE, NULL))
		return AtlHresultFromLastError();

	_sharedMemory = new CSharedMemory(name);
	if(_sharedMemory == NULL) return E_OUTOFMEMORY;

	// If it was our code which created the shared memory, then delete it.
	if(!_sharedMemory->IsCreated() || _sharedMemory->AmIFirst())
	{
		delete _sharedMemory;
		_sharedMemory = NULL;
		return E_FAIL;
	}

	return hr;
}

HRESULT OSHSource::CloseSharedMemory()
{
	if(_sharedMemory != NULL)
	{
		delete _sharedMemory;
		_sharedMemory = NULL;
	}
	return S_OK;
}

HRESULT OSHSource::StartMonitoring()
{
	if(_monitorThread)
		return S_OK;

	if(!_monitorThreadStopEvent.Create(NULL, TRUE, FALSE, NULL))
		return AtlHresultFromLastError();

	HRESULT hr = S_OK;
	_monitorThread.Attach(ATL::AtlCreateThread<OSHSource>(&MonitorThreadProc, this));
	if(!_monitorThread)
	{
		hr = AtlHresultFromLastError();
		_monitorThreadStopEvent.Close();
	}

	return hr;
}

HRESULT OSHSource::StopMonitoring()
{
	if(!_monitorThread)
		return S_OK;

	HRESULT hr = S_OK;
	if(!_monitorThreadStopEvent.Set() ||
		::WaitForSingleObject(_monitorThread, StopMonitoringTimeout) != WAIT_OBJECT_0)
	{
		if(!::TerminateThread(_monitorThread, ERROR_TIMEOUT))
			hr = AtlHresultFromLastError();
	}
	_monitorThread.Close();
	_monitorThreadStopEvent.Close();

	return hr;
}

DWORD WINAPI OSHSource::MonitorThreadProc(OSHSource* self)
{
	ATLTRACE_TS(atlTraceOSHSource, TraceLevel::Info,
		"OSHSource::MonitorThreadProc() : start\r\n");

	::CoInitializeEx(NULL, COINIT_MULTITHREADED); //COINIT_APARTMENTTHREADED, COINIT_MULTITHREADED

	DWORD currentBitmapNumber = self->_sharedMemory->GetDwordValue(
		OSHSource::BitmapNumberVariableName, OSHSource::InvalidBitmapNumber);
	if(currentBitmapNumber != OSHSource::InvalidBitmapNumber)
		self->FireOnShow();

	bool closing = false;
	bool stoping = false;
	while(true)
	{
		DWORD newBitmapNumber = OSHSource::InvalidBitmapNumber;
		DWORD result = self->_sharedMemory->WaitForValueChange(OSHSource::BitmapNumberVariableName,
			currentBitmapNumber, &newBitmapNumber, (HANDLE)self->_closeEvent, 
			self->_monitorThreadStopEvent, INFINITE);

		closing = (WaitForSingleObject(self->_closeEvent, 0) == WAIT_OBJECT_0);
		stoping = (WaitForSingleObject(self->_monitorThreadStopEvent, 0) == WAIT_OBJECT_0);
		if(closing || stoping)
			break;

		// Value has changed
		if(result == WAIT_OBJECT_0)
		{
			currentBitmapNumber = newBitmapNumber;
			if(newBitmapNumber == OSHSource::InvalidBitmapNumber)
			{
				self->FireOnHide();
				continue;
			}
			
			self->FireOnRefresh();
		}
		else
		{
			// Closing
			if(result == WAIT_OBJECT_0 + 1 ||
				result == WAIT_OBJECT_0 + 2)
				break;

			ATLTRACE_TS(atlTraceOSHSource, TraceLevel::Error,
				"OSHSource::MonitorThreadProc() : unexpected error: 0x%08X\r\n",
				AtlHresultFromWin32(result));
			break;
		}
	}

	if(closing && !stoping)
		self->FireOnHide();

	::CoUninitialize();

	ATLTRACE_TS(atlTraceOSHSource, TraceLevel::Info,
		"OSHSource::MonitorThreadProc() : stop\r\n");

	return 0;
}

void OSHSource::FireOnShow()
{
	if(_visible) return;
	ATL::CCritSecLock lock(_eventFireSection);
	if(_visible) return;

	CComPtr<OSHLibrary::IBitmap> oshBitmap;
	HRESULT hr = GetVisibleBitmap(&oshBitmap);
	if(SUCCEEDED(hr))
	{
		_visible = true;
		Fire_OnShow(oshBitmap);
	}
}

void OSHSource::FireOnHide()
{
	if(!_visible) return;
	ATL::CCritSecLock lock(_eventFireSection);
	if(!_visible) return;

	_visible = false;
	Fire_OnHide();
}

void OSHSource::FireOnRefresh()
{
	ATL::CCritSecLock lock(_eventFireSection);

	CComPtr<OSHLibrary::IBitmap> oshBitmap;
	HRESULT hr = GetVisibleBitmap(&oshBitmap);
	if(SUCCEEDED(hr))
	{
		if(!_visible)
		{
			_visible = true;
			Fire_OnShow(oshBitmap);
		}
		else
			Fire_OnRefresh(oshBitmap);
	}
}

STDMETHODIMP OSHSource::WaitForBitmap(LONG dwMilliseconds, OSHLibrary::IBitmap** ppBitmap)
{
	return WaitForBitmapEx(dwMilliseconds, 0, ppBitmap);
}

STDMETHODIMP OSHSource::WaitForBitmapEx(LONG dwMilliseconds, int cancelHandle, OSHLibrary::IBitmap** ppBitmap)
{
	if(!ppBitmap) return E_POINTER;
	*ppBitmap = NULL;

	if(!IsOpened())
		return E_UNEXPECTED;

	DWORD currentBitmapNumber = _sharedMemory->GetDwordValue(
		OSHSource::BitmapNumberVariableName, OSHSource::InvalidBitmapNumber);
	//ATLASSERT(bitmapNumber != OSHSource::InvalidBitmapNumber);

	while(true)
	{
		DWORD newBitmapNumber = OSHSource::InvalidBitmapNumber;
		DWORD result = _sharedMemory->WaitForValueChange(OSHSource::BitmapNumberVariableName,
			currentBitmapNumber, &newBitmapNumber,
			(HANDLE)_closeEvent, (HANDLE)(INT_PTR)cancelHandle, dwMilliseconds);

		// Value has changed
		if(result == WAIT_OBJECT_0)
		{
			if(newBitmapNumber == OSHSource::InvalidBitmapNumber)
			{
				currentBitmapNumber = newBitmapNumber;
				continue;
			}
			
			return GetVisibleBitmap(ppBitmap);
		}
		else
		{
			// Closing
			if(result == WAIT_OBJECT_0 + 1)
				return E_FAIL;

			// Canceled
			if(result == WAIT_OBJECT_0 + 2)
				return E_FAIL;

			return AtlHresultFromWin32(result);
		}
	}

	return E_FAIL;
}

STDMETHODIMP OSHSource::GetVisibleBitmap(OSHLibrary::IBitmap** ppBitmap)
{
	if(!ppBitmap) return E_POINTER;
	*ppBitmap = NULL;

	if(!IsOpened())
		return E_UNEXPECTED;

	HRESULT hr = S_OK;

	int bitmapNumber = (int)_sharedMemory->GetDwordValue(
		OSHSource::BitmapNumberVariableName, OSHSource::InvalidBitmapNumber);
	//ATLASSERT(bitmapNumber != OSHSource::InvalidBitmapNumber);
	if(bitmapNumber == OSHSource::InvalidBitmapNumber)
		return E_FAIL;

	ATLTRACE_TS(atlTraceOSHSource, TraceLevel::Verbose,
		"OSHSource::GetVisibleBitmap() : Getting bitmap #%d...\r\n", bitmapNumber);

	wchar_t bitmapBufferVariableName[CSharedMemory::MAX_VAR_NAME_LENGTH];
	wchar_t bitmapRefCountVariableName[CSharedMemory::MAX_VAR_NAME_LENGTH];
	wchar_t bitmapTimeVariableName[CSharedMemory::MAX_VAR_NAME_LENGTH];

	wsprintf(bitmapBufferVariableName, OSHSource::BitmapBufferVariableFormat, bitmapNumber);
	wsprintf(bitmapRefCountVariableName, OSHSource::BitmapRefCountVariableFormat, bitmapNumber);
	wsprintf(bitmapTimeVariableName, OSHSource::BitmapTimeVariableFormat, bitmapNumber);

	if(!_sharedMemory->InterlockedTestIncrement(bitmapRefCountVariableName, -1))
	{
		//ATLASSERT(false);
		return E_FAIL;
	}

	SYSTEMTIME time;
	DWORD size = sizeof(SYSTEMTIME);
	if(!_sharedMemory->GetValue(bitmapTimeVariableName, &time, &size))
	{
		ATLASSERT(false);
		 return E_FAIL;
	}

	void* bitmapPtr = _sharedMemory->GetPointer(bitmapBufferVariableName);
	ATLASSERT(bitmapPtr);
	if(!bitmapPtr) return E_FAIL;

	//CComPtr<OSHLibrary::IBitmap> bitmap;
	//hr = bitmap.CoCreateInstance(__uuidof(OSHBitmap));
	//if(FAILED(hr)) return hr;

	//ATLTRACE_TS(atlTraceOSHBitmap, TraceLevel::Verbose,
	//	"OSHSource::GetVisibleBitmap([0x%08X]) : Created a new bitmap - 0x%08X\r\n", this, bitmap.p);

	//CComPtr<OSHLibrary::IBitmapControl> bitmapControl;
	//hr = bitmap->QueryInterface(&bitmapControl);
	//if(FAILED(hr)) return hr;

	//hr = bitmapControl->Initialize(this, bitmapNumber, (int)(INT_PTR)bitmapPtr, (int)(INT_PTR)&time);
	//if(FAILED(hr)) return hr;

	CComObject<OSHBitmap>* bitmapObject = NULL;
	hr = CComObject<OSHBitmap>::CreateInstance(&bitmapObject);
	if(FAILED(hr)) return hr;

	CComPtr<OSHLibrary::IBitmap> bitmap;
	bitmap = bitmapObject;

	ATLTRACE_TS(atlTraceOSHBitmap, TraceLevel::Verbose,
		"OSHSource::GetVisibleBitmap([0x%08X]) : Created a new bitmap - 0x%08X\r\n", this, bitmapObject);

	hr = bitmapObject->Initialize(this, bitmapNumber, bitmapPtr, &time);
	if(FAILED(hr)) return hr;

	hr = bitmap.CopyTo(ppBitmap);
	
	bitmap = NULL;

	return hr;
}

HRESULT OSHSource::OnBitmapClose(OSHBitmap& bitmap)
{
	if(!IsOpened())
		return E_UNEXPECTED;

	HRESULT hr = S_OK;
	if(_isOwner)
	{
		if(_saveOnBitmapClose)
		{
			wchar_t bitmapTimeVariableName[CSharedMemory::MAX_VAR_NAME_LENGTH];
			wsprintf(bitmapTimeVariableName, OSHSource::BitmapTimeVariableFormat, bitmap.GetBitmapNumber());

			SYSTEMTIME created = bitmap.GetCreated();
			if(!_sharedMemory->SetValue(bitmapTimeVariableName, &created, sizeof(SYSTEMTIME)))
				return E_FAIL;

			hr = ActivateBitmapSlot(bitmap.GetBitmapNumber());
		}
	}
	else
	{
		wchar_t bitmapRefCountVariableName[CSharedMemory::MAX_VAR_NAME_LENGTH];
		wsprintf(bitmapRefCountVariableName, OSHSource::BitmapRefCountVariableFormat, bitmap.GetBitmapNumber());
		if(!_sharedMemory->InterlockedTestDecrement(bitmapRefCountVariableName, -1, 0))
		{
			ATLASSERT(false);
			return E_FAIL;
		}
	}

	return hr;
}

HRESULT OSHSource::ActivateBitmaps()
{
	if(!IsOpened())
		return E_UNEXPECTED;

	if(_lastActiveBitmapNumber != OSHSource::InvalidBitmapNumber)
		return ActivateBitmapSlot(_lastActiveBitmapNumber);
	else
		return E_FAIL;
}

HRESULT OSHSource::DeactivateBitmaps()
{
	if(!IsOpened())
		return E_UNEXPECTED;

	return ActivateInvalidBitmapSlot(_lastActiveBitmapNumber);
}

HRESULT OSHSource::CheckBitmapSize(int width, int height)
{
	if(_sharedMemory == NULL) return E_UNEXPECTED;
	if(width <= 0 || height <= 0) return E_INVALIDARG;
	
	DWORD size = _sharedMemory->GetDwordValue(OSHSource::BitmapSizeVariableName, 0);
	if(size == 0) return E_UNEXPECTED;

	WORD maxWidth = LOWORD(size);
	WORD maxHeight = HIWORD(size);
	
	return width <= maxWidth && height <= maxHeight ? S_OK : E_FAIL;
}


/// <summary>
/// Checks that the shared memory has allocated memory for the bitmaps of proper size.
/// If the shared memory has not already been initialized it allocates variables for
/// _maxNumberOfBitmaps number of bitmaps of the specified (bitmapSize) size.
/// </summary>
HRESULT OSHSource::EnsureBitmapSize(CSize bitmapSize)
{
	if(_sharedMemory == NULL) return E_FAIL;
	if(bitmapSize.cx <= 0 || bitmapSize.cx >= MAXWORD ||
		bitmapSize.cy <= 0 || bitmapSize.cy >= MAXWORD)
		return E_INVALIDARG;

	// Check bitmap size. If the size of the bitmaps is zero shared memory was not initialized.
	HRESULT hr = S_OK;
	DWORD size = _sharedMemory->GetDwordValue(OSHSource::BitmapSizeVariableName, 0);
	if(size != 0)
	{
		WORD width = LOWORD(size);
		WORD height = HIWORD(size);
		if(width != bitmapSize.cx || height != bitmapSize.cy)
		{
			hr = FreeBitmapVariables(_maxNumberOfBitmaps, _firstBitmapNumber);
			ATLASSERT(SUCCEEDED(hr));
			if(FAILED(hr)) return hr;
			size = 0;
		}
	}

	if(size == 0)
	{
		hr = AllocateBitmapVariables(bitmapSize, _maxNumberOfBitmaps, _firstBitmapNumber);
		if(FAILED(hr)) return hr;
	}

	return S_OK;
}

HRESULT OSHSource::FreeBitmapVariables(int numberOfBitmaps, DWORD firstBitmapNumber)
{
	if(_sharedMemory == NULL) return E_FAIL;

	ATLTRACE_TS(atlTraceOSHSource, TraceLevel::Verbose, "OSHSource::FreeBitmapVariables()\r\n");

	for(int i = 0; i < numberOfBitmaps; i++)
	{
		wchar_t bitmapBufferVariableName[CSharedMemory::MAX_VAR_NAME_LENGTH];
		wchar_t bitmapRefCountVariableName[CSharedMemory::MAX_VAR_NAME_LENGTH];
		wchar_t bitmapTimeVariableName[CSharedMemory::MAX_VAR_NAME_LENGTH];

		wsprintf(bitmapBufferVariableName, OSHSource::BitmapBufferVariableFormat, firstBitmapNumber + i);
		wsprintf(bitmapRefCountVariableName, OSHSource::BitmapRefCountVariableFormat, firstBitmapNumber + i);
		wsprintf(bitmapTimeVariableName, OSHSource::BitmapTimeVariableFormat, firstBitmapNumber + i);

		_sharedMemory->DeleteValue(bitmapRefCountVariableName);
		_sharedMemory->DeleteValue(bitmapBufferVariableName);
		_sharedMemory->DeleteValue(bitmapTimeVariableName);
	}
	
	// Set the size of the bitmaps to zero like it was not not initialized.
	_sharedMemory->SetDwordValue(OSHSource::BitmapSizeVariableName, 0);
	
	return S_OK;
}

HRESULT OSHSource::AllocateBitmapVariables(CSize bitmapSize, int numberOfBitmaps, DWORD firstBitmapNumber)
{
	if(_sharedMemory == NULL) return E_FAIL;
	if(numberOfBitmaps <= 1) return E_INVALIDARG;
	if(firstBitmapNumber == 0 || firstBitmapNumber > MAXDWORD - numberOfBitmaps) return E_INVALIDARG;

	ATLTRACE_TS(atlTraceOSHSource, TraceLevel::Verbose, "OSHSource::AllocateBitmapVariables()\r\n");

	HRESULT hr = S_OK;

	DWORD variableSize = sizeof(BITMAPINFOHEADER);
	variableSize += bitmapSize.cx * bitmapSize.cy * 4;	// 4 bytes per pixel

	for(int i = 0; i < numberOfBitmaps; i++)
	{
		wchar_t bitmapBufferVariableName[CSharedMemory::MAX_VAR_NAME_LENGTH];
		wchar_t bitmapRefCountVariableName[CSharedMemory::MAX_VAR_NAME_LENGTH];
		wchar_t bitmapTimeVariableName[CSharedMemory::MAX_VAR_NAME_LENGTH];

		wsprintf(bitmapBufferVariableName, OSHSource::BitmapBufferVariableFormat, firstBitmapNumber + i);
		wsprintf(bitmapRefCountVariableName, OSHSource::BitmapRefCountVariableFormat, firstBitmapNumber + i);
		wsprintf(bitmapTimeVariableName, OSHSource::BitmapTimeVariableFormat, firstBitmapNumber + i);

		if(!_sharedMemory->AddValue(bitmapBufferVariableName, variableSize))
		{
			hr = E_FAIL;
			break;
		}
		else
		{
			BITMAPINFOHEADER* bitmapHeader = 
				reinterpret_cast<BITMAPINFOHEADER*>(_sharedMemory->GetPointer(bitmapBufferVariableName));
			if(bitmapHeader == NULL)
			{
				hr = E_FAIL;
				break;
			}

			bitmapHeader->biSize = sizeof(BITMAPINFOHEADER);
			bitmapHeader->biWidth = bitmapSize.cx;
			bitmapHeader->biHeight = bitmapSize.cy;
			bitmapHeader->biPlanes = 1;
			bitmapHeader->biBitCount = 32;
			bitmapHeader->biCompression = BI_RGB;
			bitmapHeader->biSizeImage = bitmapSize.cx * bitmapSize.cy * 4;
		}

		if(!_sharedMemory->AddDwordValue(bitmapRefCountVariableName, (DWORD)OSHSource::InvalidRefCountValue))
		{
			hr = E_FAIL;
			break;
		}

		SYSTEMTIME time = {0};
		if(!_sharedMemory->AddValue(bitmapTimeVariableName, sizeof(SYSTEMTIME), &time))
		{
			hr = E_FAIL;
			break;
		}

	}

	if(FAILED(hr)) return hr;

	DWORD size = MAKELONG(bitmapSize.cx, bitmapSize.cy);
	if(!_sharedMemory->SetDwordValue(OSHSource::BitmapSizeVariableName, size))
		return E_FAIL;

	return S_OK;
}


HRESULT OSHSource::GetLastUsedBitmapSlot(DWORD* bitmapNumber, BITMAPINFOHEADER** bitmapHeader, void** bitmapData)
{
	if(_sharedMemory == NULL) return E_FAIL;
	if(bitmapNumber == NULL || bitmapHeader == NULL || bitmapData == NULL) return E_INVALIDARG;

	*bitmapNumber = NULL;
	*bitmapHeader = NULL;
	*bitmapData = NULL;

	DWORD lastBitmapNumber = _sharedMemory->GetDwordValue(
		OSHSource::BitmapNumberVariableName, OSHSource::InvalidBitmapNumber);

	DWORD nextBitmapNumber = OSHSource::InvalidBitmapNumber;

	if(lastBitmapNumber == OSHSource::InvalidBitmapNumber)
		nextBitmapNumber = _firstBitmapNumber;
	else
	{
		if(lastBitmapNumber >= _firstBitmapNumber + _maxNumberOfBitmaps - 1)
			nextBitmapNumber = _firstBitmapNumber;
		else
			nextBitmapNumber = lastBitmapNumber + 1;
	}

	wchar_t bitmapBufferVariableName[CSharedMemory::MAX_VAR_NAME_LENGTH];
	wchar_t bitmapRefCountVariableName[CSharedMemory::MAX_VAR_NAME_LENGTH];

	wsprintf(bitmapBufferVariableName, OSHSource::BitmapBufferVariableFormat, nextBitmapNumber);
	wsprintf(bitmapRefCountVariableName, OSHSource::BitmapRefCountVariableFormat, nextBitmapNumber);

	LONG lastRefCountValue = 0;
	if(!_sharedMemory->InterlockedCompareExchange(bitmapRefCountVariableName,
		(DWORD)OSHSource::InvalidRefCountValue, 0, reinterpret_cast<LONG*>(&lastRefCountValue)))
		return E_FAIL;

	// TODO: wait until refcount == 0
	if(lastRefCountValue >= 0)
	{
		// Block OSH sources from using this bitmap.
		if(!_sharedMemory->SetDwordValue(bitmapRefCountVariableName, (DWORD)OSHSource::InvalidRefCountValue))
		{
			ATLASSERT(false);
			return E_FAIL;
		}

		if(lastRefCountValue != 0)
			::Sleep(10);
	}

	void* bitmapVariablePtr = _sharedMemory->GetPointer(bitmapBufferVariableName);
	if(bitmapVariablePtr == NULL)
	{
		ATLASSERT(false);
		return E_FAIL;
	}

	*bitmapNumber = nextBitmapNumber;
	*bitmapHeader = reinterpret_cast<BITMAPINFOHEADER*>(bitmapVariablePtr);
	*bitmapData = reinterpret_cast<BYTE*>(bitmapVariablePtr) + sizeof(BITMAPINFOHEADER);

	return S_OK;
}


HRESULT OSHSource::ActivateBitmapSlot(DWORD bitmapNumber)
{
	if(_sharedMemory == NULL) return E_FAIL;

	wchar_t bitmapRefCountVariableName[CSharedMemory::MAX_VAR_NAME_LENGTH];
	wsprintf(bitmapRefCountVariableName, OSHSource::BitmapRefCountVariableFormat, bitmapNumber);


	if(!_sharedMemory->SetDwordValue(bitmapRefCountVariableName, 0))
		return E_FAIL;

	//ATLTRACE_TS(atlTraceOSHSource, TraceLevel::Verbose,
	//	"OSHSource::ActivateBitmapSlot(%d)\r\n", bitmapNumber);

	if(!_sharedMemory->SetDwordValue(OSHSource::BitmapNumberVariableName, bitmapNumber))
		return E_FAIL;

    return S_OK;
}

HRESULT OSHSource::ActivateInvalidBitmapSlot(OUT DWORD& lastActiveBitmapNumber)
{
	if(_sharedMemory == NULL) return E_FAIL;

	lastActiveBitmapNumber = 
		_sharedMemory->GetDwordValue(OSHSource::BitmapNumberVariableName, OSHSource::InvalidBitmapNumber);

	//ATLTRACE_TS(atlTraceOSHSource, TraceLevel::Verbose,
	//	"OSHSource::ActivateInvalidBitmapSlot()\r\n");

	if(!_sharedMemory->SetDwordValue(OSHSource::BitmapNumberVariableName, OSHSource::InvalidBitmapNumber))
		return E_FAIL;

    return S_OK;
}

